package com.maas.util.mybatis.jpa.littlenb.meta;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Lob;

import com.littlenb.mybatisjpa.annotation.CodeEnum;
import com.littlenb.mybatisjpa.type.CodeType;
import com.littlenb.mybatisjpa.type.IntCodeEnumTypeHandler;
import com.littlenb.mybatisjpa.type.StringCodeEnumTypeHandler;
import com.maas.util.mybatis.jpa.littlenb.common.JackPersistentUtil;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.type.BooleanTypeHandler;
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
import org.apache.ibatis.type.EnumTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;


/**
 * mybatis column元数据类型
 *
 * @author svili
 * @data 2017年5月6日
 *
 */
public class MybatisColumnMeta {

	/** 是否为主键 */
	private boolean primaryKey;

	/** Java fieldName */
	private String property;

	private String columnName;

	/** fieldType */
	private Class<?> type;

	/** mybatis jdbcTypeAlias */
	private String jdbcTypeAlias;

	/** mybatis jdbcType */
	private JdbcType jdbcType;

	/**
	 *类信息
	 */
	private PersistentMeta persistentMeta;

	private static Set<String> keyWordSet = new HashSet<>();

	static
	{

	}

	/** mybatis typeHandler */
	private Class<? extends TypeHandler<?>> typeHandlerClass;

	/** 持久化字段 */
	private Field field;

	public MybatisColumnMeta(Field field,PersistentMeta persistentMeta) {
		this.field = field;
		// 初始化
		this.property = field.getName();
		this.columnName = JackPersistentUtil.getColumnName(field);
		this.type = field.getType();
		this.jdbcTypeAlias = ColumnMetaResolver.resolveJdbcAlias(field);
		this.jdbcType = ColumnMetaResolver.resolveJdbcType(this.jdbcTypeAlias);
		this.typeHandlerClass = ColumnMetaResolver.resolveTypeHandler(field);
		this.persistentMeta = persistentMeta;
	}

	/** meta resolver */
	private static class ColumnMetaResolver {

		public static String resolveJdbcAlias(Field field) {

			Class<?> fieldType = field.getType();
			if (field.getType().isEnum()) {
				if (field.isAnnotationPresent(Enumerated.class)) {
					// 获取注解对象
					Enumerated enumerated = field.getAnnotation(Enumerated.class);
					// 设置了value属性
					if (enumerated.value() == EnumType.ORDINAL) {
						return "INTEGER";
					}
				}
				return "VARCHAR";
			}
			if (field.isAnnotationPresent(Lob.class)) {
				if (String.class.equals(fieldType)) {
					return "CLOB";
				}
			}
			if (Integer.class.equals(fieldType)) {
				return "INTEGER";
			}
			if (Double.class.equals(fieldType)) {
				return "DOUBLE";
			}
			if (Float.class.equals(fieldType)) {
				return "FLOAT";
			}
			if (String.class.equals(fieldType)) {
				return "VARCHAR";
			}
			// date类型需声明
			if (java.util.Date.class.isAssignableFrom(fieldType)) {
				return "TIMESTAMP";
			}
			return null;
		}

		public static JdbcType resolveJdbcType(String alias) {
			if (alias == null) {
				return null;
			}
			try {
				return JdbcType.valueOf(alias);
			} catch (IllegalArgumentException e) {
				throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
			}
		}

		/*@SuppressWarnings("unchecked")
		public static Class<? extends TypeHandler<?>> resolveTypeHandler(Field field) {
			Class<? extends TypeHandler<?>> typeHandler =null;
			if (field.getType().isEnum()) {
				if (field.isAnnotationPresent(Enumerated.class)) {
					// 获取注解对象
					Enumerated enumerated = field.getAnnotation(Enumerated.class);
					// 设置了value属性
					if (enumerated.value() == EnumType.ORDINAL) {
						//return (Class<? extends TypeHandler<?>>) EnumOrdinalTypeHandler.class;
						return typeHandler;
					}
				}
				//return (Class<? extends TypeHandler<?>>) EnumTypeHandler.class;
			}

			Class<? extends TypeHandler<?>> typeHandlerClass = null;
			if (field.getType().equals(Boolean.class)) {
				typeHandlerClass = (Class<? extends TypeHandler<?>>) BooleanTypeHandler.class;
			}
			return typeHandlerClass;
		}
	}*/

		@SuppressWarnings({"unchecked", "rawtypes"})
		public static Class<? extends TypeHandler<?>> resolveTypeHandler(Field field) {
			if (field.isAnnotationPresent(CodeEnum.class)) {
				CodeEnum codeEnum = field.getAnnotation(CodeEnum.class);
				if (Objects.equals(codeEnum.value(), CodeType.INT)) {
					IntCodeEnumTypeHandler typeHandler = new IntCodeEnumTypeHandler(field.getType());
					return (Class<? extends TypeHandler<?>>) typeHandler.getClass();
				}
				if (Objects.equals(codeEnum.value(), CodeType.STRING)) {
					StringCodeEnumTypeHandler typeHandler = new StringCodeEnumTypeHandler(field.getType());
					return (Class<? extends TypeHandler<?>>) typeHandler.getClass();
				}
			}
			if (field.getType().isEnum()) {
				if (field.isAnnotationPresent(Enumerated.class)) {
					// 获取注解对象
					Enumerated enumerated = field.getAnnotation(Enumerated.class);
					// 设置了value属性
					if (enumerated.value() == EnumType.ORDINAL) {
						EnumOrdinalTypeHandler<? extends Enum<?>> typeHandler = new EnumOrdinalTypeHandler(
								field.getType());
						return (Class<? extends TypeHandler<?>>) typeHandler.getClass();
					}
				}
				EnumTypeHandler<? extends Enum<?>> typeHandler = new EnumTypeHandler(field.getType());
				return (Class<? extends TypeHandler<?>>) typeHandler.getClass();
			}

			Class<? extends TypeHandler<?>> typeHandlerClass = null;
			if (field.getType().equals(Boolean.class)) {
				typeHandlerClass = (Class<? extends TypeHandler<?>>) BooleanTypeHandler.class;
			}
			return typeHandlerClass;
		}
	}

	// getter
	public boolean isPrimaryKey() {
		return primaryKey;
	}

	public String getProperty() {
		return property;
	}

	public String getColumnName() {
	    if(keyWordSet.contains(columnName))
	    {
	        return "`" + columnName + "`";
	    }
		return columnName;
	}

	public Class<?> getType() {
		return type;
	}

	public String getJdbcTypeAlias() {
		return jdbcTypeAlias;
	}

	public JdbcType getJdbcType() {
		return jdbcType;
	}

	public Class<? extends TypeHandler<?>> getTypeHandlerClass() {
		return typeHandlerClass;
	}

	public Field getField() {
		return field;
	}

}
